---
title: Forwarding Refs
description: "Forwarding Ref values in ReScript and React"
canonical: "/docs/react/forwarding-refs"
section: "Guides"
order: 2
---

# Forwarding Refs

<Intro>

Ref forwarding is a technique for automatically passing a [React.ref](./refs-and-the-dom.mdx) through a component to one of its children. This is typically not necessary for most components in the application. However, it can be useful for some kinds of components, especially in reusable component libraries. The most common scenarios are described below.

</Intro>

## Why Ref Forwarding?

Consider a FancyButton component that renders the native button DOM element:

```res example
// FancyButton.res

@react.component
let make = (~children) => {
  <button className="FancyButton">
    children
  </button>
}
```

React components hide their implementation details, including their rendered output. Other components using FancyButton **usually will not need** to obtain a ref to the inner button DOM element. This is good because it prevents components from relying on each other’s DOM structure too much.

Although such encapsulation is desirable for application-level components like `FeedStory` or `Comment`, it can be inconvenient for highly reusable “leaf” components like `FancyButton` or `MyTextInput`. These components tend to be used throughout the application in a similar manner as a regular DOM button and input, and accessing their DOM nodes may be unavoidable for managing focus, selection, or animations.

There are currently two strategies on forwarding refs through a component. In ReScript and React we strongly recommend **passing your ref as a prop**, but there is also a dedicated API called `React.forwardRef`.

We will discuss both methods in this document.

## Forward Refs via Props

A `React.ref` can be passed down like any other prop. The component will take care of forwarding the ref to the right DOM element.

**No new concepts to learn!**

In the example below, `FancyInput` defines a prop `inputRef` that will be forwarded to its `input` element:

```res example
// App.res

module FancyInput = {
  @react.component
  let make = (~children, ~inputRef: ReactDOM.domRef) =>
    <div> <input type_="text" ref=inputRef /> children </div>
}

@send external focus: Dom.element => unit = "focus"

@react.component
let make = () => {
  let input = React.useRef(Nullable.null)

  let focusInput = () =>
    input.current->Nullable.forEach(input => input->focus)

  let onClick = _ => focusInput()

  <div>
    <FancyInput inputRef={ReactDOM.Ref.domRef(input)}>
      <button onClick> {React.string("Click to focus")} </button>
    </FancyInput>
  </div>
}
```

We use the `ReactDOM.domRef` type to represent our `inputRef`. We pass our ref in the exact same manner as we'd do a DOM `ref` attribute (`<input ref={ReactDOM.Ref.domRef(myRef)} />`).

## [Discouraged] React.forwardRef

**Note:** We discourage this method since it will likely go away at some point, and doesn't yield any obvious advantages over the previously mentioned ref prop passing.

`React.forwardRef` offers a way to "emulate a `ref` prop" within custom components. Internally the component will forward the passed `ref` value to the target DOM element instead.

This is how the previous example would look like with the `React.forwardRef` approach:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
// App.res

module FancyInput = {
  @react.component
  let make = React.forwardRef((~className=?, ~children, ref) =>
    <div>
      <input
        type_="text"
        ?className
        ref=?{Nullable.toOption(ref)->Option.map(ReactDOM.Ref.domRef)}
      />
      children
    </div>
  )
}

@send external focus: Dom.element => unit = "focus"

@react.component
let make = () => {
  let input = React.useRef(Nullable.null)

  let focusInput = () =>
    input.current->Nullable.forEach(input => input->focus)

  let onClick = _ => focusInput()

  <div>
    <FancyInput className="fancy" ref=input>
      <button onClick> {React.string("Click to focus")} </button>
    </FancyInput>
  </div>
}
```

```js
import * as React from "react";
import * as Belt_Option from "rescript/lib/es6/belt_Option.js";
import * as Caml_option from "rescript/lib/es6/caml_option.js";

var make = React.forwardRef(function (props, ref) {
  return React.createElement(
    "div",
    undefined,
    React.createElement("input", {
      ref: Belt_Option.map(
        ref == null ? undefined : Caml_option.some(ref),
        function (prim) {
          return prim;
        },
      ),
      className: props.className,
      type: "text",
    }),
    props.children,
  );
});

var FancyInput = {
  make: make,
};

function App(props) {
  var input = React.useRef(null);
  var onClick = function (param) {
    Belt_Option.forEach(
      Caml_option.nullable_to_opt(input.current),
      function (input) {
        input.focus();
      },
    );
  };
  return React.createElement(
    "div",
    undefined,
    React.createElement(make, {
      className: "fancy",
      children: React.createElement(
        "button",
        {
          onClick: onClick,
        },
        "Click to focus",
      ),
      ref: input,
    }),
  );
}
```

</CodeTab>

**Note:** Our `@react.component` decorator transforms our labeled argument props within our `React.forwardRef` function in the same manner as our classic `make` function.

This way, components using `FancyInput` can get a ref to the underlying `input` DOM node and access it if necessary—just like if they used a DOM `input` directly.

## Note for Component Library Maintainers

**When you start using ref forwarding in a component library, you should treat it as a breaking change and release a new major version of your library**. This is because your library likely has an observably different behavior (such as what refs get assigned to, and what types are exported), and this can break apps and other libraries that depend on the old behavior.
